package com.tokaplot;

import android.os.SystemClock;

public class RenderInfo {

    public float[] dataValue = null;
    public float[] dataTime = null;
    public int nData;
    public int nDataStart;

    public long rLastTime;
    private int rLastData;  // Index of the last data point
    public int rMaxTime;    // Number of time samples (widget width)
    public float rMax, rMin, rCurrent;

    public float dataFirstValue;
    public float dataLastValue;
    public float dataLastTime;

    public float getBufferPercentage() {
        return (float) nData / (float) rMaxTime;
    }

    RenderInfo(int pMaxTime) {

        // Set to default values
        rMax = -TokaplotService.maxVal;
        rMin = TokaplotService.maxVal;
        rCurrent = 0.0f;
        rLastData = 0;
        rMaxTime = pMaxTime;
        rLastTime = SystemClock.elapsedRealtime();

        dataFirstValue = 0.0f;
        dataLastValue = 0.0f;
        dataLastTime = 0.0f;

        // Create arrays
        nData = 0;
        nDataStart = 0;
        dataValue = new float[rMaxTime];
        dataTime = new float[rMaxTime];
    }

    void make(String type, long sampRate, boolean useLocalRange) {

        // Incrementally update RenderInfo for sensor
        if (TokaplotService.sensorTable.containsKey(type) && type != TokaplotService.s_none) {

            // Local variables
            int i, iData;
            float avVal = 0.0f;
            float avValN = 0.0f;

            // Update global list
            long now = SystemClock.elapsedRealtime();
            float dt = (float) (now - rLastTime) / (float) sampRate;
            for (i = nDataStart; i < nDataStart + nData; i++) {
                dataTime[i % rMaxTime] += dt;
            }
            dataLastTime += dt;
            if (dataLastTime > TokaplotService.maxDataLength)
                dataLastTime = TokaplotService.maxDataLength;
            rLastTime = now;

            // Check bounds
            if (nData > 0 && dataTime[(nDataStart + nData - 1) % rMaxTime] > (float) rMaxTime) {

                // Remove value
                nData--;
                i = (nDataStart + nData) % rMaxTime;
                dataLastValue = dataValue[i];
                dataLastTime = dataTime[i];

                // Check max/min
                float rMaxMin = dataValue[i];
                if (rMaxMin == rMax || rMaxMin == rMin) {

                    // Re-calculate bounds
                    rMax = -TokaplotService.maxVal;
                    rMin = TokaplotService.maxVal;
                    for (i = nDataStart; i < nDataStart + nData; i++) {
                        rMaxMin = dataValue[i % rMaxTime];
                        if (rMaxMin > rMax)
                            rMax = rMaxMin;
                        if (rMaxMin < rMin)
                            rMin = rMaxMin;
                    }
                }

            }

            // Calculate average in time bucket
            SensorInfo s = TokaplotService.sensorTable.get(type);
            if (s == null) // Should never happen, but just to be safe
                return;

            int iMax = (s.getDataCurrent() + TokaplotService.maxDataLength - rLastData) % TokaplotService.maxDataLength;
            synchronized (this) {
                for (i = rLastData; i < rLastData + iMax; i++) {

                    // Get data value
                    iData = i % TokaplotService.maxDataLength;
                    avVal += s.sDataVal[iData];
                    avValN += 1.0f;
                }
            }
            rLastData = s.getDataCurrent();

            // Process data
            if (avValN != 0.0 && avVal < TokaplotService.maxVal) {
                avVal /= avValN;

                // Calculate range
                if (useLocalRange) {

                    // Use local range
                    if (avVal > rMax)
                        rMax = avVal;
                    if (avVal < rMin)
                        rMin = avVal;

                } else {

                    // Use global range
                    rMax = TokaplotService.getSensorMax(type);
                    rMin = TokaplotService.getSensorMin(type);
                }

                // Set current (un-smoothed) data value
                rCurrent = TokaplotService.getSensorCurrent(type);
                if (rCurrent > rMax)
                    rMax = rCurrent;
                if (rCurrent < rMin)
                    rMin = rCurrent;

                // Add new value
                nData++;
                nDataStart = (rMaxTime + nDataStart - 1) % rMaxTime;
                dataValue[nDataStart] = avVal;
                dataTime[nDataStart] = 0.0f;
                dataFirstValue = avVal;
            }
        }
    }

    void makeAll(String type, long sampRate, boolean useLocalRange) {

        // Rebuild RenderInfo for sensor
        rMax = -TokaplotService.maxVal;
        rMin = TokaplotService.maxVal;
        rCurrent = 0.0f;

        dataLastValue = TokaplotService.maxVal;
        dataLastTime = TokaplotService.maxDataLength;

        if (TokaplotService.sensorTable.containsKey(type) && type != TokaplotService.s_none) {

            // Local variables
            int i, t;
            long now = SystemClock.elapsedRealtime();
            java.util.SortedMap<Integer, Float> avValuesMap = new java.util.TreeMap<Integer, Float>();
            java.util.SortedMap<Integer, Float> avValuesNMap = new java.util.TreeMap<Integer, Float>();

            // Get sensor
            SensorInfo s = TokaplotService.sensorTable.get(type);
            if (s == null) // Should never happen, but just to be safe
                return;

            // Bucket data
            Float mapVal = 0.0f;
            synchronized (this) {

                for (i = 0; i < TokaplotService.maxDataLength; i++) {

                    // Get data value
                    if (s.sDataTime[i] != 0) {
                        t = (int) ((float) (now - s.sDataTime[i]) / (float) (sampRate));

                        // Set time bucket using map key as time
                        if (t >= 0 && t < rMaxTime) {
                            if ((mapVal = avValuesMap.get(t)) != null) {
                                avValuesMap.put(t, mapVal + s.sDataVal[i]);
                            } else {
                                avValuesMap.put(t, s.sDataVal[i]);
                            }

                            if ((mapVal = avValuesNMap.get(t)) != null) {
                                avValuesNMap.put(t, mapVal + 1.0f);
                            } else {
                                avValuesNMap.put(t, 1.0f);
                            }
                        } else {
                            if (t >= 0 && t < dataLastTime) {
                                dataLastValue = s.sDataVal[i];
                                dataLastTime = t;
                            }
                        }
                    }
                }
            }

            // Take average
            for (Integer key : avValuesMap.keySet()) {
                avValuesMap.put(key, avValuesMap.get(key) / avValuesNMap.get(key));
            }

            // Calculate range
            if (useLocalRange) {

                // Use local range
                for (Integer key : avValuesMap.keySet()) {

                    // Otherwise get value for key
                    mapVal = avValuesMap.get(key);
                    if (Math.abs(mapVal) < TokaplotService.maxVal) {
                        if (mapVal > rMax)
                            rMax = mapVal;
                        if (mapVal < rMin)
                            rMin = mapVal;
                    }
                }
            } else {

                // Use global range
                rMax = TokaplotService.getSensorMax(type);
                rMin = TokaplotService.getSensorMin(type);
            }

            // Set current (un-smoothed) data value
            rCurrent = TokaplotService.getSensorCurrent(type);
            if (rCurrent > rMax)
                rMax = rCurrent;
            if (rCurrent < rMin)
                rMin = rCurrent;

            // Clear data
            nData = 0;
            nDataStart = 0;
            for (i = 0; i < rMaxTime; i++) {
                dataValue[i] = 0.0f;
                dataTime[i] = 0.0f;
            }

            // Add to data
            for (Integer key : avValuesMap.keySet()) {
                mapVal = avValuesMap.get(key);

                if (Math.abs(mapVal) < TokaplotService.maxVal) {
                    dataValue[nData % rMaxTime] = mapVal;       // %rMaxTime is just for safety
                    dataTime[nData % rMaxTime] = (float) key;   // %rMaxTime is just for safety
                    nData++;
                }
            }

            // Calculate first/last values for rendering
            if (nData == 0) {

                // Special case if list is empty
                dataFirstValue = rCurrent;
                dataLastValue = rCurrent;
                dataLastTime = rMaxTime;

                rMax = rCurrent;
                rMin = rCurrent;
            } else {
                if (dataLastValue == TokaplotService.maxVal) {

                    // Special case if first value doesn't fall outside time range
                    dataLastValue = dataValue[nData - 1];
                    dataLastTime = dataTime[nData - 1];
                }
                dataFirstValue = dataValue[0];
            }
        }
    }
}